package middleware.p2p.global.monitor;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.util.Iterator;
import java.util.LinkedList;

import javax.ejb.Singleton;
import javax.ejb.Stateful;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;

import middleware.p2p.log.MiddlewareMonitorEvent;
import middleware.p2p.log.QueueType;
import middleware.p2p.log.VectorClock;

/**
 * Session Bean implementation class MonitorQueueManager
 */
@Singleton(name="MonitorQueue")
@TransactionManagement(TransactionManagementType.BEAN)
public class MonitorQueueManager implements MonitorQueueManagerLocal {
	private LinkedList<MiddlewareMonitorEvent> incomingTDMonitor;
	private LinkedList<MiddlewareMonitorEvent> incomingWFMonitor;
	private VectorClock vclock;
    /**
     * Default constructor. 
     */
    public MonitorQueueManager() {
        super();
        this.vclock=new VectorClock();
        this.incomingWFMonitor= new LinkedList<MiddlewareMonitorEvent>();
        this.incomingTDMonitor= new LinkedList<MiddlewareMonitorEvent>();
    }

    private void deliver(MiddlewareMonitorEvent event,QueueType type) {
    	Iterator<String> keyIt=event.getVclock().getAllKey().iterator();
    	while (keyIt.hasNext()) {
    		String cKey=keyIt.next();
    		int currValue=this.vclock.getClock(cKey);
    		int remValue=event.getVclock().getClock(cKey);
    		if (remValue>currValue) {
    			this.vclock.setClock(cKey, remValue);
    		} else if((currValue==0)&&(remValue==0)) {
    			this.vclock.setClock(cKey, 0);
    		}
    	}
    	//System.out.println(event.getVclock());
    	File globalLog=new File(".."+File.separator+"logs"+File.separator+"middleware_"+type+"_status.log");
		try {
			PrintStream outFile = new PrintStream(new FileOutputStream(globalLog, true));
	    	outFile.println(event.toXmlString());
	    	outFile.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
    }
    private boolean deliverCompare(MiddlewareMonitorEvent right) {
    	int comp=vclock.compareTo(right.getVclock());
		boolean gapDetect=vclock.detectGap(right.getVclock());
		boolean proxDetect=vclock.detectProximity(right.getVclock());
		//System.out.println("COMPARE=>"+comp+" || GAP =>"+gapDetect+" || PROX =>"+proxDetect);
		//System.out.println("$$$$$$$$$$$$$$$$$$$$$$$"+vclock.toString()+"\n********************"+right.getVclock().toString());
		if ((comp==-1)&&(!gapDetect)&&(proxDetect)) {
			return true;
		} else if((comp==0)&&(proxDetect)) {
			return true;
		} else {
			return false;
		}
    }
	@Override
	public boolean enqueue(QueueType type, MiddlewareMonitorEvent event) {
		/*if (intervalTime==null) {
			TimerConfig tConfig=new TimerConfig("Monitor Queue Sorter Timeout",true);
			intervalTime=timerService.createIntervalTimer(60000,1000,tConfig);
			//System.out.println("Timer created");
		}*/
		try{
			if (deliverCompare(event)) {
				//System.out.println("========>immediate delivery \n");
				deliver(event,type);
				boolean found=true;
				while (found) {
					Iterator<MiddlewareMonitorEvent> itEv=this.incomingTDMonitor.iterator();
					found=false;
					QueueType qType=QueueType.TQueue;
					while (itEv.hasNext()) {
						MiddlewareMonitorEvent currEvent=itEv.next();
						if (deliverCompare(currEvent)) {
							itEv.remove();
							deliver(currEvent,qType);
							found=true;
							break;
						}
					}
					if (found) continue;
					itEv=this.incomingWFMonitor.iterator();
					qType=QueueType.WFQueue;
					while (itEv.hasNext()) {
						MiddlewareMonitorEvent currEvent=itEv.next();
						if (deliverCompare(currEvent)) {
							itEv.remove();
							deliver(currEvent,qType);
							found=true;
							break;
						}
					}
				}
			} else {
				switch (type) {
					case TQueue:
						incomingTDMonitor.add(event);
						break;
					case WFQueue:
						incomingWFMonitor.add(event);
						break;
					default:
						return false;
				}							
			}
			return true;
		}
		catch(IllegalStateException e){
			e.printStackTrace();
			return false;
		}
	}
	@Override
	public boolean resetClock(String cid) {
		//System.out.println("Reset Clock "+cid);
		this.vclock.setClock(cid, 0);
		return true;
	}
}
